package com.zmx.b2c.pay.service;

import com.zmx.b2c.order.common.CompletableFutureThreadPool;
import com.zmx.b2c.order.dto.B2COrderDto;
import com.zmx.b2c.order.dto.B2COrderItemDto;
import com.zmx.b2c.order.service.B2COrderService;
import com.zmx.b2c.pay.dto.PaymentDto;
import com.zmx.b2c.pay.entity.Payment;
import com.zmx.b2c.pay.repository.PaymentRepository;
import com.zmx.b2c.pay.service.mapper.PaymentMapper;
import com.zmx.b2c.stock.dto.ProductStockDto;
import com.zmx.b2c.stock.service.ProductStockService;
import com.zmx.common.common.contents.ProductStockContents;
import com.zmx.common.common.enums.Enum;
import com.zmx.common.common.exception.BusinessException;
import com.zmx.common.common.service.BaseService;
import com.zmx.common.common.utils.Lock.RedissonLock;
import com.zmx.product.api.ProductApi;
import com.zmx.product.dto.ProductDto;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zhaomxr
 */
@Slf4j
@Service
public class PaymentService extends BaseService<Payment, PaymentDto, PaymentRepository, PaymentMapper> {

    @Autowired
    private RedissonLock redissonLock;
    @Autowired
    private B2COrderService b2COrderService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductApi productApi;
    @Autowired
    private ProductStockService productStockService;
    @Autowired
    private PaymentService paymentService;

    /**
     * 取订单支付记录
     *
     * @param orderId
     * @return
     */
    public List<PaymentDto> findByOrders(Long orderId) {
        List<Payment> payments = repository.findByOrders(orderId);
        return entityToDto(payments);
    }

    /**
     * 去支付
     * @param OrderSn
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Integer> toPay(String OrderSn) {
        Map<String, Integer> result = new HashMap<String, Integer>(16);
        B2COrderDto b2COrderDto = b2COrderService.getOrderByCode(OrderSn);

        //扣减商品库存
        paymentService.deductStock(b2COrderDto);

        //吊起微信小程序支付
        //暂时没写
        return result;
    }

    /**
     * 扣减商品库存
     * @param b2COrderDto
     */
    @Transactional(rollbackFor = Exception.class)
    public void deductStock(B2COrderDto b2COrderDto) {
        List<B2COrderItemDto> b2COrderItemDtos = b2COrderDto.getB2COrderItemDtos();
        List<Long> productIds = b2COrderItemDtos.stream().map(B2COrderItemDto::getProductId).collect(Collectors.toList());

        //多线程查询商品及商品库存，加快处理速度
        CompletableFutureThreadPool completableFutureThreadPool = new CompletableFutureThreadPool();
        ThreadPoolExecutor pool = completableFutureThreadPool.poolExecutor();
        List<CompletableFuture<?>> futureList = new ArrayList<CompletableFuture<?>>(2);

        CompletableFuture<List<ProductDto>> productDtosfuture = CompletableFuture.supplyAsync(() -> {
            List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));
            return productDtos;
        }, pool);
        futureList.add(productDtosfuture);
        CompletableFuture<List<ProductStockDto>> productStockDtosfuture = CompletableFuture.supplyAsync(() -> {
            List<ProductStockDto> productStockDtos = this.getByProductIdIn(productIds.toArray(new Long[0]));
            return productStockDtos;
        }, pool);
        futureList.add(productStockDtosfuture);
        //所有任务都执行完成并返回
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();

        List<ProductDto> productDtos = productDtosfuture.join();
        List<ProductStockDto> productStockDtos = productStockDtosfuture.join();

        for (B2COrderItemDto b2COrderItemDto : b2COrderItemDtos) {
            Long productId = b2COrderItemDto.getProductId();
            List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                return Objects.equals(productDto.getId(), b2COrderItemDto.getProductId());
            }).collect(Collectors.toList());
            ProductDto productDto = productDtoList.get(0);

            List<ProductStockDto> productStockDtoList = productStockDtos.stream().filter(productStockDto -> {
                return Objects.equals(productStockDto.getProductId(), b2COrderItemDto.getProductId());
            }).collect(Collectors.toList());
            ProductStockDto productStockDto = productStockDtoList.get(0);

            RLock rLock = null;
            try {
                //redisson，RLock红锁
                rLock = redissonLock.lock(ProductStockContents.PRODUCT_STOCK_LOCK_KEY + productId);
                boolean lock = rLock.tryLock(0, 5, TimeUnit.SECONDS);
                if (lock) {
                    //ts时间，版本校验
                    boolean tsChange = this.tsChange(productStockDto.getId(), productStockDto.getTs());
                    if (tsChange) {
                        throw new BusinessException("由于提交过于频繁，请稍后重试!");
                    }

                    int quantity = b2COrderItemDto.getQuantity();
                    if (quantity > productStockDto.getAvailableStock()) {
                        throw new BusinessException("商品[" + productDto.getName() + "]库存不足");
                    }

                    //更新库存
                    productStockService.updateStock(quantity, productId);
                } else {
                    throw new BusinessException("由于提交过于频繁，请稍后重试!");
                }
            } catch (InterruptedException e) {
                log.error("扣减库存失败：", e);
                throw new BusinessException("由于提交过于频繁，请稍后重试!");
            } finally {
                if (rLock != null) {
                    rLock.unlock();
                }
            }

            log.info("update product AllocatedStock productId:{} quantity:{}" + b2COrderItemDto.getProductId(), b2COrderItemDto.getQuantity());
        }
    }
    /**
     * 去支付 秒杀
     * @param OrderSn
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Integer> toPaySkill(String OrderSn) {
        //吊起微信小程序支付
        //暂时没写
        B2COrderDto b2COrderDto = b2COrderService.getOrderByCode(OrderSn);
        List<B2COrderItemDto> b2COrderItemDtos = b2COrderDto.getB2COrderItemDtos();

        //已扣减库存商品
        Map<String, Integer> decrementMap = new HashMap<String, Integer>(b2COrderItemDtos.size());
        for (B2COrderItemDto b2COrderItemDto : b2COrderItemDtos) {
            //扣减redis中库存
            String key = ProductStockContents.PRODUCT_STOCK_NUM_KEY_PREFIX + b2COrderItemDto.getProductId();
            int quantity = b2COrderItemDto.getQuantity();
            Long increment = redisTemplate.opsForValue().increment(key, ~(quantity - 1));
            decrementMap.put(b2COrderItemDto.getProductId().toString(), quantity);

            if (increment < 0) {
                break;
            }
            log.info("update product AllocatedStock productId:{} quantity:{}" + b2COrderItemDto.getProductId(), b2COrderItemDto.getQuantity());
        }

        return decrementMap;
    }

    /**
     * 支付回调
     * @param OrderSn
     */
    @Transactional(rollbackFor = Exception.class)
    public void callback(String OrderSn) {
        //更新商品已售库存
        B2COrderDto b2COrderDto = b2COrderService.getOrderByCode(OrderSn);

        //更新支付记录状态为 1-已支付
        Payment payment = repository.findLastPaymentByOrderId(b2COrderDto.getId());
        PaymentDto paymentDto = new PaymentDto();
        paymentDto.setId(payment.getId());
        paymentDto.setPaymentDate(new Date());
        paymentDto.setStatus(Enum.PaymentStatus.paid.getCode());
        this.update(paymentDto);

        //回写订单支付状态
        b2COrderService.payAfterRewriteOrder(b2COrderDto, BigDecimal.ZERO);
    }

    /**
     * 批量查询商品库存
     * @param productIds
     * @return
     */
    private List<ProductStockDto> getByProductIdIn(Long[] productIds) {
        List<ProductStockDto> productStockDtos = new ArrayList<ProductStockDto>(productIds.length);

        for (Long productId : productIds) {
            ProductStockDto productStockDto = productStockService.getByProductId(productId);
            productStockDtos.add(productStockDto);
        }
        if (productStockDtos == null || productStockDtos.size() == 0) {
            productStockDtos = productStockService.getByProductIdIn(productIds);
        }

        return productStockDtos;
    }
}
